/**
 * 通用工具类
 * @version 1.0.1
 * @method stdMessage(success, message, data = '', code = 0) 返回标准消息对象
 * @method random(min, max=0) 生成随机数
 */
class EsUtils {

    /**
     * 返回标准消息对象
     * @param {Boolean} $success 是否成功
     * @param {String} message 消息内容
     * @param {Object} data 数据内容
     * @param {Number} code 状态码 200=成功,401=未授权(未登录),403=禁止访问,500=服务器错误
     * @see https://tool.lu/httpcode/
     */
    static stdMessage(success, message, data = '', code = 0) {
        return {
            success: success,
            message: message,
            data: data,
            code: code,
        }
    }
    /**
     * 生成随机数
     * @example random(1,10) 生成 1...10之间的随机数（包括1和10）。random(10) 生成 0...10 之间的随机数
     */
    static random(min, max = 0) {
        if (max == 0) {
            max = min;
            min = 0;
        }
        return Math.round(Math.random() * (max - min)) + min;
    }

    /**
     * 返回数组中一个或多个随机的键
     */
    static arrayRandom(array, number = 1) {
        let idx = Math.floor((Math.random() * array.length));
        let data = [], array2 = [...array];
        // splice() 方法会改变原始数组
        for (let i = 0; i < number; i++) {
            data.push(array2.splice(idx, 1)[0]);
        }
        return number == 1 ? data[0] : data;
    }

    /**
     * 将数组打乱
     * 注意：此方法会改变原始数组！
     */
    static arrayShuffle(array) {
        // sort() 方法会改变原始数组！
        array.sort(() => {
            return Math.random() > 0.5 ? -1 : 1;
        })
        return array;
    }

    static arrayCopy(arr, result) {
        result = result || [];
        for (var i = 0; i < arr.length; i++) {
            if (arr[i] instanceof Array) {
                result[i] = [];
                EsUtils.arrayCopy(arr[i], result[i]);
            } else {
                result[i] = arr[i];
            }
        }
        return result;
    }
    /**
     * 缩减字符串
     */
    static shortText(text, length = 10, lastLength = 0, ellipsis = '...') {
        if (!text || text.length < length) {
            return text;
        }
        if (lastLength == 0) {
            return text.substring(0, length) + ellipsis;
        }
        return text.substring(0, length - lastLength) + ellipsis + text.substr(-lastLength);
    }
    /**
     * 字节单位转换
     */
    static bytesToSize(size) {
        if (bytes === 0) {
            return '0 B';
        }
        var k = 1024, // or 1024
            sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
            i = Math.floor(Math.log(bytes) / Math.log(k));

        return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];
    }
    /**
     * 数字单位
     */
    static numberUnit(num = 0) {
        if (!num) {
            return 0;
        }
        num = parseInt(num);
        if (num > 10000) {
            return Math.round(num / 1000) + '万';
        }
        return num;
    }
    /**
     * 数值限制
     */
    static numberLimit(value, min = 0, max = 10) {
        if (isNaN(value)) {
            return min;
        }
        value = Number(value)
        if (value < min) {
            return min;
        }
        if (value > max) {
            return max;
        }
        return value;
    }
    // 对Date的扩展，将 Date 转化为指定格式的String 
    // 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符， 
    // 年(y)可以用 1-4 个占位符，毫秒(S)只能用 1 个占位符(是 1-3 位的数字) 
    // 例子： 
    // (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423 
    // (new Date()).Format("yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18 
    static timeFormat(fmt, text = null) {
        let date = text ? new Date(text) : new Date();
        //author: meizz 
        let o = {
            "M+": date.getMonth() + 1, //月份 
            "d+": date.getDate(), //日 
            "h+": date.getHours(), //小时 
            "m+": date.getMinutes(), //分 
            "s+": date.getSeconds(), //秒 
            "q+": Math.floor((date.getMonth() + 3) / 3), //季度 
            "S": date.getMilliseconds() //毫秒 
        };
        if (/(y+)/.test(fmt)) {
            fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
        }
        for (let k in o)
            if (new RegExp("(" + k + ")").test(fmt)) {
                fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k])
                    .length)));
            }
        return fmt;
    }
    // 获取浏览器参数
    static getQueryString(name = '', defaultValue = null, queryString = null) {
        if (queryString == null) {
            queryString = window.location.search;
            if (queryString.length == 0) {
                return name.length == 0 ? {} : defaultValue;
            }
        }
        let usp = new URLSearchParams(queryString);
        if (name.length > 0) {
            return usp.get(name) || defaultValue;
        }
        let obj = {};
        usp.forEach((v, k) => {
            obj[k] = v;
        })
        return obj;
    }
    // 获取cookie参数
    static getCookie(name, defval = null, cookie = '') {
        if (!cookie) {
            cookie = document.cookie;
        }
        if (!cookie) {
            return defval;
        }
        // cookie1=value; cookie2=value; cookie3=value;
        var params = cookie.split(';'),
            pair = [];
        for (let param of params) {
            pair = param.split('=');
            if (pair[0].trim() == name) {
                return pair[1];
            }
        }
        return defval;
    }
    /**
     * 复制到剪贴板
     */
    static copyText(text) {
        if (navigator) {
            navigator.clipboard.writeText(text);
            return;
        }
        let input = document.createElement('input');
        input.value = text;
        document.body.appendChild(input);
        input.select();
        document.execCommand('copy');
        document.body.removeChild(input);
    }
    /**
     * 字符串转为Dom对象
     */
    static strToDom(htmlStr) {
        // 方法1: 使用 Fragment。效率高
        let frag = document.createRange().createContextualFragment(htmlStr);
        return frag.firstChild;
        // 方法2: 使用 DOMParser。效率低
        // return new DOMParser().parseFromString(htmlStr,'text/html');
        // 方法3: 使用常规方法
        // let node=document.createElement('DIV');
        // node.innerHTML=htmlStr;
        // return node.childNodes;
    }
    /**
     * 创建标准消息模式的 Axios 实例
     * 是否启用 mock 数据（如果使用 mock 数据，则 url 不能拼接 BaseUrl，否则路径会匹配不上）
     * process.env.VUE_APP_API_BASE_URL // .env 文件主要用于多人协同的团队开发环境，我是一个人全栈，所以不使用 .env 文件，省得每次更改都要重新编译。。。
     */
    static getStdAjax(baseURL = '', timeout = 60000, fnGetToken = null) {
        const myAxios = axios.create({
            baseURL: baseURL,
            timeout: timeout,
        })

        // axios 添加请求拦截
        myAxios.interceptors.request.use(config => {

            if (!fnGetToken && typeof (LoginState) != 'object') {
                fnGetToken = LoginState.getLoginInfo;
            }
            if (typeof (fnGetToken) == 'function') {
                const token = fnGetToken();
                if (token && typeof (token) == "string") {
                    config.headers.Authorization = token;
                }
            }
            console.log('添加请求拦截', config.headers.Authorization);
            return config;
        })

        // axios 添加输出拦截
        myAxios.interceptors.response.use(response => {
            console.log('添加输出拦截', response);
            if (response.data.success == undefined) {
                return Promise.reject(EsUtils.stdMessage(false, "服务器返回的数据格式错误"))
            }
            return Promise.resolve(response.data);
        }, error => {
            return Promise.reject(EsUtils.stdMessage(false, error.message))
        })
        return myAxios;
    }

    /**
     * 操作 Iframe 元素
     * @param {String} operate 操作类型: reload=刷新&load=新页面&empty=清空
     */
    static handleIframe(iframe, operate, link = '') {
        if (!iframe) {
            console.log('onHandleIframe iframe is null.');
            return;
        }
        if (operate == 'reload') {
            console.log('onHandleIframe reload.');
            iframe.contentWindow.location.reload(true);
            return;
        }
        console.log('onHandleIframe link = ', link);
        // load 操作。把 iframe 指向空白页面，这样可以释放大部分内存。
        iframe.src = 'about:blank';
        try {
            iframe.contentWindow.document.write('');
            iframe.contentWindow.document.clear();
        } catch (e) { }
        if (link) {
            iframe.src = link;
        }
    }

    /**
     * 校验数据
     * @param {Object} rules 
     * @param {Object} data 
     * @returns 
     */
    static validate(rules, data) {
        if (!data) {
            return EsUtils.stdMessage(false, '数据无效');
        }
        let defaultRegex = {
            'alpha': /^[A-Za-z]+$/,
            'alphaNum': /^[A-Za-z0-9]+$/,
            'alphaDash': /^[A-Za-z0-9\-\_]+$/,
            'chs': /^[\u4e00-\u9fa5]+$/,
            'chsAlpha': /^[\u4e00-\u9fa5a-zA-Z]+$/,
            'chsAlphaNum': /^[\u4e00-\u9fa5a-zA-Z0-9]+$/,
            'chsDash': /^[\u4e00-\u9fa5a-zA-Z0-9\_\-]+$/,
            'mobile': /^1[3-9]\d{9}$/,
            'idCard': /(^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$)/,
            'zip': /\d{6}/,
            'number': /^\d+$/,
            'email': /^[A-Za-z\d]+([-_.][A-Za-z\d]+)*@([A-Za-z\d]+[-.])+[A-Za-z\d]{2,4}$/,
            'ip4': /(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}/,
            'ip6': /^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?$/,
            'url': /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/,
            // 特殊字符限制  斜杠、反斜杠、双引号、单引号
            'specialCharCheck': /["\\'/\s]+/,
            // 密码  至少包含大小写字母、数字和符号两种以上组合，6-15位长度
            'password': /(?!^[0-9]+$)(?!^[A-z]+$)(?!^[^A-z0-9]+$)^.{6,15}$/,
        };
        let keys = Object.keys(data), errors = [], title = '', isExistsKey = false, rule = null;
        for (let k in rules) {
            rule = rules[k];
            console.log(k, rule);
            title = k;
            if (k.indexOf('|') > -1) {
                let [k, title] = k.split('|', 2);
            }
            isExistsKey = keys.indexOf(k) > -1;
            if (rule.is_required && (!isExistsKey || data[k] == null)) {
                errors.push(`${title} 是必填项`)
                continue;
            }
            if (rule.is_email && isExistsKey && !(defaultRegex.email.test(data[k].toString()))) {
                errors.push(`${title} 不是有效的电子邮件`)
                continue;
            }
            if (rule.is_mobile && isExistsKey && !(defaultRegex.mobile.test(data[k].toString()))) {
                errors.push(`${title} 不是有效的手机号码`)
                continue;
            }
            if (rule.is_number && isExistsKey && !(defaultRegex.number.test(data[k].toString()))) {
                errors.push(`${title} 不是有效的数字`)
                continue;
            }
            if (rule.is_password && isExistsKey && !(defaultRegex.password.test(data[k].toString()))) {
                errors.push(`${title} 不是有效的密码`)
                continue;
            }
            if (rule.confirm && isExistsKey) {
                if ((keys.indexOf(rule.confirm) > -1) && (data[k] != data[rule.confirm])) {
                    errors.push(`${title} 两次密码输入不一致`)
                    continue;
                }
            }
            if (rule.max && isExistsKey) {
                if (data[k].toString().length > rule.max) {
                    errors.push(`${title} 长度超过 ${rule.max} 个字符`)
                    continue;
                }
            }
            if (rule.min && isExistsKey) {
                if (data[k].toString().length < rule.min) {
                    errors.push(`${title} 长度少于 ${rule.min} 个字符`)
                    continue;
                }
            }
        }
        if (errors.length > 0) {
            return EsUtils.stdMessage(false, errors[0], errors);
        }
        return EsUtils.stdMessage(true);
    }
}